import { AppBase, CtxBase, Vite, Err, resErr, commonErr, Winston, LOG } from 'koagun'
import mongoose from 'mongoose'
import Bull from 'bull'
import { LRUCache } from 'lru-cache'
import send from 'koa-send'
import Router from 'koa-router'
import { Config } from '../config/app_default.cfg'
import 'koa-body'
import { User } from '../src/services/user/user'
import { Doctor } from './services/doctor/doctor'
import { Patient } from './services/patient/patient'

export type Ctx = CtxExtension & CtxBase
export type CtxExtension = {
    app: App,
    current: {
        token: string,
        creator: number,
        name: string,
        roleUuids: string[]
    }
}

export type Services = {
    user: User
    doctor: Doctor
    patient: Patient
}

export type App = AppExtension & AppBase
export type AppExtension = {
    router: Router<object, Ctx>,
    config: Config,
    // redis: Redis,
    lru: LRUCache<any, any>,
    // influx: {
    //     db: InfluxDB,
    //     query: QueryApi
    // }
    // redlock: Redlock,
    queue: Bull.Queue,
    // @ts-ignore
    addJob: <S extends keyof Services, A extends keyof Services[S], F extends keyof Services[S][A]>(
        s: S, a: A, f: F,
        // @ts-ignore
        params: Parameters<Services[S][A][F]>,
        options?: Bull.JobOptions
    ) => Promise<void>,
    vite: Vite,
    services: Services
}

export async function createApp(config: Config) {
  return AppBase.createApp<AppExtension>({
    port: config.LISTEN_PORT,
    mountRouter: true,
    mountBodyParser: true,
    koaBodyOptions: { multipart: true },

    errHandlerFn: async function (err, ctx: Ctx) {
      console.log(err)
      if (err instanceof Err) {
        resErr(ctx, err)
      } else {
        // await sendRobotMessage(config, 'alert', '服务器内部错误', `路径：${ctx.path}\n错误：${err?.message ? err?.message : JSON.stringify(err)}`)
        resErr(ctx, new Err(commonErr['INTERNAL_ERROR']))
      }
      return
    },

    onMounted: async function (app) {
      app.config = config
      Winston.init(app.config.LOGGER)

      app.lru = new LRUCache({ max: 5000 })

      // if (!config.REDIS.ignore) {
      //     const redis = new Redis(config.REDIS)
      //     app.redis = redis
      //     app.redlock = new Redlock([redis])
      //     app.queue = new Bull(config.APP_NAME, { redis: config.REDIS })
      //     // @ts-ignore
      //     app.addJob = async <S extends keyof Services, A extends keyof Services[S], F extends keyof Services[S][A]>(
      //         s: S, a: A, f: F,
      //         // @ts-ignore
      //         params: Parameters<Services[S][A][F]>,
      //         options?: Bull.JobOptions
      //     ) => {
      //         await app.queue.add({ s, a, f, params }, options)
      //     }
      // }

      if (!config.MONGODB.ignore) {
        await mongoose.connect(config.MONGODB.uri, { dbName: 'covison', writeConcern: { w: 'majority' } })
      }

      // if (!config.INFLUX.ignore) {
      //     const influx = new InfluxDB(config.INFLUX.options)
      //     app.influx = {
      //         db: influx,
      //         query: influx.getQueryApi(config.INFLUX.org)
      //     }
      // }

      app.router.all(/^\/api\/v1/, async (ctx: Ctx, next) => {
        ctx.app = app
        await next()
      })

      app.vite = new Vite({ ...config.VITEX, env: config.ENV === 'production' ? 'production' : 'default' })
      app.use(await app.vite.getMiddleware())

      app.services = {
        user: new User(app),
        doctor: new Doctor(app),
        patient: new Patient(app),
      }

      app.use(async (ctx: Ctx) => {
        const pub = '/public'
        if (ctx.path.startsWith(pub)) {
          await send(ctx, ctx.path, { immutable: true })
        }
      })
    }
  })
}